package cn.remex.db.rsql;

import cn.remex.core.RemexContext;
import cn.remex.core.exception.ServiceCode;
import cn.remex.db.Container;
import cn.remex.db.Database;
import cn.remex.db.DbCvo;
import cn.remex.db.DbRvo;
import cn.remex.db.exception.RsqlExecuteException;
import cn.remex.db.model.SysUser;
import cn.remex.db.rsql.connection.RDBManager;
import cn.remex.db.rsql.model.Modelable;
import cn.remex.db.rsql.model.ModelableImpl;
import cn.remex.db.rsql.transactional.RsqlTransaction;
import cn.remex.db.sql.FieldType;
import cn.remex.db.sql.SqlType;
import cn.remex.db.sql.WhereRuleOper;
import cn.remex.db.utils.Assert;
import cn.remex.db.utils.CoreSvo;
import cn.remex.db.utils.Judgment;
import cn.remex.db.utils.StringHelper;
import cn.remex.db.utils.aop.AOPCaller;
import cn.remex.db.utils.aop.AOPFactory;
import cn.remex.db.utils.aop.DynamicProxy;
import cn.remex.db.utils.reflect.ReflectUtil;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.aop.AfterReturningAdvice;

import javax.persistence.*;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 本类对外交互的参数只有两个cvo、rvo。 其中cvo封装了读取json的基本参数，并且 在cvo中的parameter变量中必须含有
 * sqlCvoName变量的索引
 */
public class RsqlContainer implements Container, RsqlConstants {
    private String spaceName = RDBManager.DEFAULT_SPACE;


    private static class AfterAdvice implements AfterReturningAdvice {

        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            String methodName = method.getName();
            Class<?> returnType = method.getReturnType();
//            boolean isGetMethod = args.length == 0 && (methodName.startsWith("get") || (methodName.startsWith("is") && (boolean.class.equals(returnType) || Boolean.class.equals(returnType))) );
            boolean isSetMethod = args.length == 1 && methodName.startsWith("set");
            @SuppressWarnings("unused")
            boolean isRetCollection = false, isRetObject = false;
            if (isSetMethod
                    && (CoreSvo.valLocal(StoreObjectFlied) == null ? true : (Boolean) CoreSvo.valLocal(StoreObjectFlied))) {
                if ((SYS_Method_setDataStatus + SYS_Method_setId).contains(methodName)) {
                    //;
                } else {
                    Modelable m = (Modelable) target;
                    m._setDataStatus(DS_needSave);
                    //将调用了set方法的属性保存起来，在store的时候使用，节约资源
                    if (!Collection.class.isAssignableFrom(returnType)
                            && !Modelable.class.isAssignableFrom(returnType))
                        m._addModifyFileds(StringHelper.lowerFirstLetter(methodName.substring(3)));
                }
            }
        }
    }

    private static AfterAdvice afterAdvice = new AfterAdvice();
    /**
     * spring 的bean工厂<br>
     */
//    private static AOPFactory DBBeanFactory = new AOPFactory(new AOPCaller(null) {
//        private static final long serialVersionUID = 865574373191977031L;
//
//        @SuppressWarnings("unchecked")
//        @Override
//        public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy)
//                throws Throwable {
//            // 查找
//            // getIaop().beforeMethod();
//            Object result = proxy.invokeSuper(obj, args); // 方法在下面
//            String methodName = method.getName();
//            Class<?> returnType = method.getReturnType();
////            boolean isGetMethod = args.length == 0 && (methodName.startsWith("get") || (methodName.startsWith("is") && (boolean.class.equals(returnType) || Boolean.class.equals(returnType))) );
//            boolean isSetMethod = args.length == 1 && methodName.startsWith("set");
//            @SuppressWarnings("unused")
//            boolean isRetCollection = false, isRetObject = false;
//            if (isSetMethod
//                    && (CoreSvo.valLocal(StoreObjectFlied) == null ? true : (Boolean) CoreSvo.valLocal(StoreObjectFlied))) {
//                if ((SYS_Method_setDataStatus + SYS_Method_setId).contains(methodName)) {
//                    //;
//                } else {
//                    Modelable m = (Modelable) obj;
//                    m._setDataStatus(DS_needSave);
//                    //将调用了set方法的属性保存起来，在store的时候使用，节约资源
//                    if (!Collection.class.isAssignableFrom(returnType)
//                            && !Modelable.class.isAssignableFrom(returnType))
//                        m._addModifyFileds(StringHelper.lowerFirstLetter(methodName.substring(3)));
//                }
//            }
//            return result;
//        }
//    });
    /**
     * 本线程是否自动获取object类型属性标示
     * false代表自动获取，true代表不自动获取，此处关键是设计给JSON序列化时避免无限循环的
     */
    private static final String FecthObjectFlied = "cn.remex.db.rsql.RsqlContainer.FecthObjectFlied";
    private static final String StoreObjectFlied = "cn.remex.db.rsql.RsqlContainer.StoreObjectFlied";

    @Override
    public boolean isLocalAutoFetchObjectFiled() {
        Object b = CoreSvo.valLocal(FecthObjectFlied);
        return (null == b ? true : (Boolean) b);
    }

    @Override
    public boolean isLocalAutoStoreObjectFiled() {
        Object b = CoreSvo.valLocal(StoreObjectFlied);
        return (null == b ? true : (Boolean) b);
    }

    /**
     * 本线程是否自动获取object类型属性标识
     *
     * @param b true代表自动获取，false代表不自动获取，此处关键是设计给JSON序列化时避免无限循环的
     */
    @Override
    public void setLocalAutoFecthObjectFiled(boolean b) {
        CoreSvo.putLocal(FecthObjectFlied, b);
    }

    /**
     * 本线程是否自动存储object类型属性标识
     *
     * @param b true代表自动获取，false代表不自动获取，此处关键是设计给JSON序列化时避免无限循环的
     */
    @Override
    public void setLocalAutoStoreObjectFlied(boolean b) {
        CoreSvo.putLocal(StoreObjectFlied, b);
    }

    /**
     * 直接获取非缓存dbbean
     *
     * @return T
     */
    @Override
    public <T extends Modelable, ParentType extends Modelable> T createDBBean(final Class<T> clazz) {
        Assert.isTrue(RDBManager.getLocalSpaceConfig(spaceName).hasOrmBeanClass(clazz), ServiceCode.RSQL_BEANCLASS_ERROR, "数据库操作的类型不是继承于modelable");
        T t = DynamicProxy.getProxy(clazz, afterAdvice);
//        T t = DBBeanFactory.getBean(clazz);
        t._setAopModelBean();
        t._setDataStatus(DS_beanNew);
        return t;
    }

    //===lambda控制方式下数据库控制接口===============//
    @Override
    public <T extends Modelable, ParentType extends Modelable> DbCvo<T, ParentType> createDbCvo(Class<T> beanClass) {
        DbCvo dbCvo = new DbCvo<>(spaceName, beanClass);
        dbCvo.setContainer(this);
        return dbCvo;
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> DbCvo<T, ParentType> createDbCvo(String sqlString, Map<String, Object> params) {
        DbCvo dbCvo = new DbCvo(spaceName, obtainManualSql(sqlString), params);
        dbCvo.setContainer(this);
        return dbCvo;
    }

    //===允许直接通过会话的核心数据库控制接口===============//
    @Override
    @RsqlTransaction
    // 此方法被外部调用时需要配置该声明。该声明在本模块内部调用时无法触发事务切面的。
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> store(final T obj) {
        DbCvo dbCvo = new DbCvo<>(spaceName, obj.getClass(), SqlOper.store/*, DT_whole*/);
        return store(obj, dbCvo);//如果修改了默认值，则使用当前的
    }


    //===辅助方法，不能用于lambda控制方式，建议逐步使用lambda注入属性名及值得方式替代========//
    @Override
    @RsqlTransaction
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> deleteById(Class<T> clazz, final String id) {
        T bean = ReflectUtil.invokeNewInstance(clazz);
        bean.setId(id);
        return delete(bean);
    }

    @Override
    @RsqlTransaction
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> delete(final T o) {
        @SuppressWarnings("unchecked")
        Class<T> clazz = (Class<T>) o.getClass();
        DbCvo<T, ParentType> dbCvo = new DbCvo<>(spaceName, clazz, SqlOper.del);
        dbCvo.setOper(SqlOper.del);
        o._setDataStatus(DataStatus.removed.toString());// ReflectUtil.invokeSetter(SYS_dataStatus,
        // o,
        // DataStatus.removed.toString());
        // LHY 2015-2-17
        // dbCvo.setDataType(DT_whole);, 2016-8-21 LHY 删除dataType
        dbCvo._setSpaceName(spaceName);
        dbCvo.setId(o.getId());// dbCvo.setId((String)
        // ReflectUtil.invokeGetter(SYS_id, o)); LHY
        // 2015-2-17
        // dbCvo._initForRsqlDao(); LHY 2015-2-17 在store中已经处理
        // RsqlRvo dbRvo = RsqlExecutor.getDefaultRemexDao().executeUpdate(dbCvo);
        DbRvo<T> dbRvo = store(o, dbCvo);
        return dbRvo;
    }


    //====核心的方法 ==========================//

    /**
     * RsqlContainer C增加U更新D删除的核心方法。
     * 根据传入的对象和cvo中的相关参数存储数据
     *
     * @param obj 存储的对象
     * @return DbRvo
     */
    @RsqlTransaction
    @Override
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> store(final T obj, final DbCvo<T, ParentType> cvo) {
        // 如果对象为空 则不保存，空对象不保存
        Assert.notNull(obj, ServiceCode.RSQL_DATA_ERROR, "不能保存空对象");
        Assert.isTrue(Modelable.class.isAssignableFrom(obj.getClass()), ServiceCode.RSQL_BEANCLASS_ERROR, "数据库操作的对象不是继承于modelable");

        DbRvo<T> dbRvo;
        String beanName = StringHelper.getClassSimpleName(obj.getClass());
        Class<?> beanClass = RDBManager.getLocalSpaceConfig(spaceName).getOrmBeanClass(beanName);// 必须考虑的cgil加强,后面用这个原始beanClass替换了
        // 没有在配置文件中配置的bean不保存

        // 进入对象保存，
        // 为了避免无限递归必须检查数据状态，如需保存还需设置saving
        Object dataStatus = getDS(obj);
        if (DS_saving.equals(dataStatus))
            return null;
        else
            updateDS(obj, DS_saving);
        String obj_id = getPK(obj, false);

        /** =================保存base及外键object类型数据============ */
        // String dt = String.valueOf(null != cvo && null != cvo.getDataType() ? cvo.getDataType() : DT_whole);
        // 初始化数据条件对象
        SqlOper oper = obtainOper(obj_id, dataStatus, cvo.getOper(), cvo); // del/add/edit
        // String dcs = (null != cvo && Judgment.notEmpty(cvo.getDataColumns())) ? cvo.getDataColumns() : (obj._isAopModelBean() ? obj._getModifyFileds() : null);// 通过切面的监控优化
        if (obj._isAopModelBean() && !Judgment.nullOrBlank(obj._getModifyFileds())) {
            for (String fieldName : obj._getModifyFileds().split("[,;]")) {
                cvo._getRootColumn().withColumn(fieldName);
            }
        }

        @SuppressWarnings("unchecked")
        DbCvo<T, ParentType> dbCvo = new DbCvo<>(spaceName, (Class<T>) obj.getClass(), oper, /*dt, 2016-8-21 LHY 删除dataType*/cvo._getRootColumn());// 需要生成本地所需的DBCVO
        dbCvo.setId(obj_id);
        dbCvo.setFilter(cvo.getFilter());
        dbCvo.setBean(obj);//新建时，需要通过bean中的 generateId 获取id
        if (!SqlOper.del.equals(oper)) {
            dbCvo.putParameters(maplizeObject(obj, oper, cvo)); // 更新、插入时才需要序列化基本数据列，id，datastatus列必须处理。
        }
        // 外键对象数据
        Map<OneToOne, Modelable> map = null;
        //if (dt.contains(DT_object)) LHY 2016-8-21
        map = this.storeFKBean(beanClass, obj, dbCvo);// 保存外键对象并将更新的外键对象的Id存入DbCvo,如Person.department更新后将partment.id更新至本obj的DbCvo为partment=id

        // 执行base及object's id 保存.
        dbCvo._initForRsqlDao();// LHY 2015-1-17 挪到此处更加恰当，Dao切面捕获异常时需要用到sqlbean，必须提前初始化。
        dbRvo = RsqlExecutor.getDefaultSqlExecutor().executeUpdate(dbCvo);

        // 更新one2one 关系 ，关系只能在保存完当前对象后 更新
        updateOne2One(obj, map);

        // 对象基本数据保存成功后必须更新其id
        if (dbRvo.getStatus() && null != dbRvo.getId()) {
            obj_id = dbRvo.getId();
            this.updatePK(obj, obj_id);
            this.updateDS(obj, !SqlOper.del.equals(oper) ? DS_managed : DS_removed);
        } else {
            this.updateDS(obj, dataStatus);
        }

        /** =================检查并保存collection类型数据============ */
//        if (dt.contains(DT_collection))
        this.storeFKList(beanClass, obj, dbCvo, obj_id, beanName, oper);

        // 标记对象已经完成保存。
        // updateDS(obj, DS_managed); LHY 挪动至461
        return dbRvo;
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> query(final DbCvo<T, ParentType> dbCvo) {
        dbCvo._initForRsqlDao();// LHY 2015-1-17 挪到此处更加恰当，Dao切面捕获异常时需要用到sqlbean，必须提前初始化。
        return RsqlExecutor.getDefaultSqlExecutor().executeQuery(dbCvo);
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> update(final DbCvo<T, ParentType> dbCvo) {
        dbCvo.setOper(SqlOper.edit);
        dbCvo._initForRsqlDao();
        return RsqlExecutor.getDefaultSqlExecutor().executeUpdate(dbCvo);
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> delete(DbCvo<T, ParentType> dbCvo) {
        dbCvo.setOper(SqlOper.del);
        dbCvo._initForRsqlDao();
        return RsqlExecutor.getDefaultSqlExecutor().executeUpdate(dbCvo);
    }

    //===核心的数据操作方法-直接到DAO层=====================//
    @Override
    @RsqlTransaction
    // 此方法被外部调用时需要配置该声明。该声明在本模块内部调用时无法触发事务切面的。
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> execute(final String sql, final Map<String, Object> params) {
        DbCvo dbCvo = new DbCvo<>(spaceName, obtainManualSql(sql), params);
        dbCvo.setContainer(this);
        dbCvo._setSpaceName(spaceName);
        // dbCvo.setRowCount(0); // 现在是自动全查
        dbCvo._initForRsqlDao();// LHY 2015-1-17 挪到此处更加恰当，Dao切面捕获异常时需要用到sqlbean，必须提前初始化。
        return RsqlExecutor.getDefaultSqlExecutor().execute(dbCvo);
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> executeQuery(final String sql, final Map<String, Object> params) {
        DbCvo dbCvo = new DbCvo<>(spaceName, obtainManualSql(sql), params);
        dbCvo.setContainer(this);
        dbCvo._setSpaceName(spaceName);
        // dbCvo.setRowCount(0); // 现在是自动全查
        dbCvo._initForRsqlDao();// LHY 2015-1-17 挪到此处更加恰当，Dao切面捕获异常时需要用到sqlbean，必须提前初始化。
        if (null != params && null != params.get("RMX_beanClass"))
            dbCvo.setBeanClass((Class) params.get("RMX_beanClass"));
        return RsqlExecutor.getDefaultSqlExecutor().executeQuery(dbCvo);
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> executeQuery(final DbCvo<T, ParentType> dbCvo) {
        dbCvo._initForRsqlDao();// LHY 2015-1-17 挪到此处更加恰当，Dao切面捕获异常时需要用到sqlbean，必须提前初始化。
        return RsqlExecutor.getDefaultSqlExecutor().executeQuery(dbCvo);
    }

    @Override
    @RsqlTransaction
    // 此方法被外部调用时需要配置该声明。该声明在本模块内部调用时无法触发事务切面的。
    public <T extends Modelable, ParentType extends Modelable> DbRvo<T> executeUpdate(final String sql, final Map<String, Object> params) {
        DbCvo dbCvo = new DbCvo<>(spaceName, obtainManualSql(sql), params);
        dbCvo.setContainer(this);
        dbCvo._setSpaceName(spaceName);
        dbCvo._initForRsqlDao();// LHY 2015-1-17 挪到此处更加恰当，Dao切面捕获异常时需要用到sqlbean，必须提前初始化。
        return RsqlExecutor.getDefaultSqlExecutor().executeUpdate(dbCvo);
    }

    @Override
    public DbRvo createCall(final String callSql) {
        return RsqlExecutor.getDefaultSqlExecutor().createCall(callSql, spaceName);
    }


    //===内部控制的方法=========================//
    @Override
    public void setSpaceName(final String spaceName) {
        this.spaceName = spaceName;
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> Class<T> obtainModelClass(String beanName) {
        return (Class<T>) RDBManager.getLocalSpaceConfig(spaceName).getOrmBeanClass(beanName);
    }

    @Override
    public <T extends Modelable, ParentType extends Modelable> boolean exists(final T obj, final boolean forceCheckFromDB) {
        // ormBeans一定有getDataStatus方法，已经断言
        Object dataStatus = obj._getDataStatus();
        if (DS_saving.equals(dataStatus) || null != getPK(obj, forceCheckFromDB))
            return true;
        else
            return false;
    }

    @Override
    public String getSpaceName() {
        return spaceName;
    }

    /**
     * 保存当前对象的one2one对应外键对象属性为当前对象的id
     *
     * @param <T>
     */
    private <T> void updateOne2One(T obj, Map<OneToOne, ? extends Modelable> map) {
        map.forEach((oto, foreignBean) -> {
            if (oto != null) {
                String tgtFieldName = oto.mappedBy();
                Assert.isTrue(!Judgment.nullOrBlank(tgtFieldName), ServiceCode.ERROR, "OneToOne的mappedBy对方属性的值不能为空！");
                if (!DS_saving.equals(foreignBean._getDataStatus())) {
                    //当双方都维护oneToOne关系时，需要保存双方各自关系，导致循环保存，在此判断上一个bean的保存状态来退出循环
                    ReflectUtil.invokeSetter(tgtFieldName, foreignBean, obj);
                    Database.createDbCvo((Class<Modelable>) foreignBean.getClass(), spaceName).withColumn(tgtFieldName).ready().store(foreignBean);
                }
            }
        });
    }

    /**
     * 此方法用于根据id确定是否需要保存。 如果为空表示需要保存。
     *
     * @return boolean
     */
    private <T extends Modelable> boolean needStore(final T obj) {
        // ormBeans一定有getDataStatus方法，已经断言
        String id = getPK(obj, false);
        String ds = getDS(obj);
        if (null == id || id.compareTo("-1") <= 0 || DataStatus.needSave.equals(ds)) //id为空或小于等于-1，状态为needSave都需要保存
            return true;
        return false;
    }

    /**
     * 如果string以"SQL_"开头则从spring的配置文件Rsql_SQL中获取真实的SQL。
     */
    private String obtainManualSql(String str) {
        return str;
    }

    /**
     * 根据obj的id及dataStatus的属性确定操作类型
     *
     * @return String
     */
    private <T extends Modelable, ParentType extends Modelable> SqlOper obtainOper(final String p_id, final Object dataStatus, final SqlOper sqlOper, final DbCvo<T, ParentType> cvo) {
        boolean idForNew = RsqlUtils.modelIdIsNew(p_id);
        boolean isUpdateByWhere = cvo.isUpdateByWhere();
        if (idForNew && (!isUpdateByWhere) && (DS_beanNew.equals(dataStatus) || DS_needSave.equals(dataStatus) || SqlOper.store.equals(sqlOper)))
            return SqlOper.add;
        else if (!idForNew && (!isUpdateByWhere) && (DS_removed.equals(dataStatus) || SqlOper.del.equals(sqlOper)))
            return SqlOper.del;
        else if (isUpdateByWhere || (!idForNew && (DS_beanNew.equals(dataStatus) || DS_managed.equals(dataStatus) || DS_needSave.equals(dataStatus) || DS_part.equals(dataStatus) || SqlOper.edit.equals(sqlOper))))
            return SqlOper.edit;
        else
            throw new RsqlExecuteException(ServiceCode.RSQL_SQL_ERROR, "数据状态id及dataStatus错误！(add：id为空或者-1 && beanNew或者needSave)；del:id不能为空并且removed；edit:isUpdateByWhere=true/id不能为空 && managed或者needSave");
    }

    /**
     * 序列化对象为Map 类型为TBase，同时添加系统必备的字段
     */
    private <T extends Modelable> Map<String, Object> maplizeObject(final T o, SqlOper oper, DbCvo cvo) {
        Map<String, Object> mapFromObject = new HashMap<>();
        boolean emptyRootColumn = !cvo._getRootColumn().hasAnyColumn();

        //添加cvo的所有列
        mapFromObject.putAll(cvo.getParameters());

        Map<String, Method> baseGetters = SqlType.getGetters(o.getClass(), FieldType.TBase);
        //rootColumn中存在的列才保存（通过with*或者assign*）都可以获得设置
        baseGetters.keySet().stream()
                .filter(fieldName -> (emptyRootColumn || cvo._getRootColumn().anyMatchInRoot(fieldName))
                )
                .forEach(fieldName -> {
                    if (cvo.getParameters().containsKey(fieldName)) {//在CVO中显示赋值的，不用map出他得值 同时需要更新到bean中，这样避免assign系列方法出问题
                        ReflectUtil.invokeSetterWithDefaultTypeCoventer(o, fieldName, cvo.$V(fieldName));
                    } else {
                        Method getter = baseGetters.get(fieldName);
                        Object value = ReflectUtil.invokeMethod(getter, o);
                        mapFromObject.put(fieldName, value);
                    }

                });


        // 只有在添加的时候修改者三个字段
        Date now = new Date((System.currentTimeMillis() / 1000) * 1000);
        SysUser au = RemexContext.obtainCurUser();
        String un = au == null || Judgment.nullOrBlank(au.getUsername()) ? "NONE" : au.getUsername();
        if (oper == SqlOper.add) {
            mapFromObject.put(RsqlConstants.SYS_id, o.getId());
            mapFromObject.put(RsqlConstants.SYS_createTime, now);
            mapFromObject.put(RsqlConstants.SYS_createOperator, un);
            mapFromObject.put(RsqlConstants.SYS_ownership, un);
            ((ModelableImpl) o).setCreateTime(now);
        }
        mapFromObject.put(RsqlConstants.SYS_modifyTime, now);
        mapFromObject.put(RsqlConstants.SYS_modifyOperator, un);
        ((ModelableImpl) o).setModifyTime(now);

        return mapFromObject;
    }

    /**
     * 保存数据库表中的外键数据，在java里面这个关系以为这一个外键的java bean，指FKBean
     */
    private <T extends Modelable, ParentType extends Modelable> Map<OneToOne, Modelable> storeFKBean(final Class<?> beanClass, final T obj, final DbCvo<T, ParentType> cvo) {
        /***** 外键对象数据类型保存 ********************/
        Map<String, Method> objectGetters = SqlType.getGetters(beanClass, FieldType.TObject);
        String F_id = null;// 默认外键连接id为空
        Method getter;
        T value;
        Map<OneToOne, Modelable> map = new HashMap<>(); // 保存o2o 的信息 供保存完当前对象后更新关系。
//LHY 优化到sqlColumn里面了
//        String dcs = cvo.getDataColumns();
//        boolean hasDcs = !Judgment.nullOrBlank(dcs);// dcs已经在Store(T,DbCvo)处理了。
//        if (hasDcs) {
//            dcs = cvo.getDataColumns() + ";";
//        }
        setLocalAutoFecthObjectFiled(false);
        for (String fieldName : objectGetters.keySet()) {
            if (obj._isAopModelBean() && !cvo._getRootColumn().anyMatchInRoot(fieldName)) { //LHY 0613 dcs.indexOf(fieldName + ";") < 0
                continue;
            }
            // OneToOne otm = ReflectUtil.getAnnotation(beanClass, fieldName,
            // OneToOne.class);// 要么一对多，要么多对多

            getter = objectGetters.get(fieldName);

            // 获取外键对象
            value = (T) ReflectUtil.invokeMethod(getter, obj);

            // 如果数据库中不含有本对象则按照参数option规则递归保存外联对象
            if (null == value)
                continue;

            OneToOne oto = ReflectUtil.getAnnotation(beanClass, fieldName, OneToOne.class);// 一对一
            if (DataStatus.removed.equalsString(obj._getDataStatus())) {
                ManyToOne mto = ReflectUtil.getAnnotation(beanClass, fieldName, ManyToOne.class);// 多对一
                boolean hasCascade = (null != mto && mto.cascade().length > 0) || (null != oto && oto.cascade().length > 0);
                List<CascadeType> cascade = hasCascade ? Arrays.asList(mto.cascade().length > 0 ? mto.cascade() : oto.cascade()) : null;//ManyToOne的优先级高于OneToOne
                // 如果有级联删除的标志位，则删除
                if (hasCascade && cascade.contains(CascadeType.REMOVE)) {
                    delete(obj);
                }
            } else if (needStore(value)) {
                ManyToOne mto = ReflectUtil.getAnnotation(beanClass, fieldName, ManyToOne.class);// 多对一
                boolean hasCascade = (null != mto && mto.cascade().length > 0) || (null != oto && oto.cascade().length > 0);
                List<CascadeType> cascade = hasCascade ? Arrays.asList(mto.cascade().length > 0 ? mto.cascade() : oto.cascade()) : null;//ManyToOne的优先级高于OneToOne
                // 如果有级联保存的标志位，则保存
                if (hasCascade && cascade.contains(CascadeType.PERSIST)) {
                    if (oto != null) {// 保存o2o 的信息 供保存完当前对象后更新关系。
                        map.put(oto, value);
                    }
                    F_id = Database.getSession(this.spaceName).store(value).getId();
                }

                if (null == F_id) {
                    throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, "在非级联状态下，一对一中的外键对象没有保存！没有保存的属性为：" + fieldName);
                }
            } else { //更新关系即可
                F_id = this.getPK(value, false);
                if (oto != null) {// 保存o2o 的信息 供保存完当前对象后更新关系。
                    map.put(oto, value);
                }
            }
            // 外键对象处理完毕，无论保存与否都保存id，如果没有则id为null
            cvo.$S(fieldName, F_id);
        }// end for ObjectFields
        setLocalAutoFecthObjectFiled(true);
        return map;
    }

    /**
     * 保存数据库表中的外键list。在关系型数据库中对应为一个一对多的数据关系。
     * <p>
     * 保存有两种方式<p>主表的id存于字表的字段中，从字表的方向看为外键对象 <p>
     * 新建一个关系表，专门用于保存一对多关系。在Rsql中一第一种方式为准。
     */
    private <T extends Modelable, ParentType extends Modelable> void storeFKList(final Class<?> beanClass, final T obj, final DbCvo<T, ParentType> cvo, final Object obj_Id, final String beanName, SqlOper oper) {
        // 取得集合映射
        Map<String, Method> cGetters = SqlType.getGetters(beanClass, FieldType.TCollection);
        Map<String, Type> cFields = SqlType.getFields(beanClass, FieldType.TCollection);
//LHY 优化到sqlColumn里面了
//        String dcs = cvo.getDataColumns();
//        boolean hasDcs = !Judgment.nullOrBlank(dcs);// dcs已经在Store(T,DbCvo)处理了。
//        if (hasDcs) {
//            dcs = cvo.getDataColumns() + ";";
//        }
        cFields.keySet().stream().filter(key -> !cvo._getRootColumn().hasAnyColumn() || cvo._getRootColumn().anyMatchInRoot(key))
                .forEach(fieldName -> {
                    Type fieldType = cFields.get(fieldName);
                    Class<?> subBeanClass = ReflectUtil.getListActualType(fieldType);
                    // 读取当前bean中collection连接对象
                    @SuppressWarnings("unchecked")
                    Collection<Modelable> obj_fieldColl = (Collection<Modelable>) ReflectUtil.invokeMethod(cGetters.get(fieldName), obj);// 此行是调用的非代理方法，读取的时bean中的原始数据，没有去数据库读取。
                    if (SqlOper.add.equals(oper) && (null == obj_fieldColl || 0 == obj_fieldColl.size()))
                        return;// 如果是父对象是新增，且列表为空，则不用进行下面的操作！ LHY 2014-11-1 优化
                    if (SqlOper.edit.equals(oper) && obj._isAopModelBean() && !cvo._getRootColumn().anyMatchInRoot(fieldName)) {
                        return;// 编辑时，如果是代理对象，且dcs里没有则不用保存
                    }
                    boolean notDelOper = !(SqlOper.del.equals(oper) || DataStatus.removed.equalsString(obj._getDataStatus())); // 不是删除操作的标志位，如果oper为删除或者数据的状态为删除都代表删除。
                    if (notDelOper && null == obj_fieldColl)// 定义:在非删除情况下， Collection属性为null，即前台该属性为null或者undefined时，多对多关系不变。
                        return;
                    // 读取数据库的collection数据id，
                    @SuppressWarnings("unchecked")
                    Map<String, Modelable> db_fieldMap = (Map<String, Modelable>) RsqlUtils.doListColumn_select(spaceName, beanClass, fieldName, obj_Id).obtainObjectsMap(SYS_id, subBeanClass);
                    // 如果当前bean的collection和数据的collection都为空则不用保存
                    if ((null != obj_fieldColl && 0 == obj_fieldColl.size()) && (null == db_fieldMap || 0 == db_fieldMap.size())) // 前端该属性为0的数组，且数据库中的集合也为0时，不用进行多对多关系维护。
                        return;

                    OneToMany otm = ReflectUtil.getAnnotation(beanClass, fieldName, OneToMany.class);// 要么一对多，要么多对多
                    if (null != otm) {
                        /*************** 一对多,多方用一个属性来保存这个关系的，只需更新多方外键即可 ********/

                        String mappedBy = otm.mappedBy();
                        boolean hasCascade = otm.cascade().length > 0;
                        List<CascadeType> cascade = hasCascade ? Arrays.asList(otm.cascade()) : null;
                        if (Judgment.nullOrBlank(mappedBy))
                            return;

                        // 不为空并且 父对象不是标示为删除 则逐一保存
                        if (null != obj_fieldColl && notDelOper) { // if L2
                            boolean isPersist = hasCascade && cascade.contains(CascadeType.PERSIST);// 级联更新及保存
                            for (Modelable co : obj_fieldColl) {
                                // 一对多的多方为空则不用保存
                                if (null == co)
                                    continue;

                                String coField_id = String.valueOf(ReflectUtil.invokeGetter(SYS_id, co));

                                if (db_fieldMap.containsKey(coField_id)) {// 数据库中已经存在
                                    if (isPersist) {// 因为已经存在于数据库中，故为级联更新
                                        ReflectUtil.invokeSetter(mappedBy, co, obj);// 检查并确保外键连接
                                        Database.getSession(this.spaceName).store(co);
                                    } else if (DataStatus.needSave.equalsString(co._getDataStatus())) {
                                        throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, "在非级联状态下，一对多中的外键对象没有保存更新！");
                                    } else { // 不是级联需要保存关系 2016-1-13 LHY   2017/3/7 改用update不用store,否则会新保存未保存的非级联对象,之前留下的天坑
                                        ReflectUtil.invokeSetter(mappedBy, co, obj);// 检查并确保外键连接
                                        Database.update((Class<Modelable>) co.getClass(), this.spaceName).assignColumn(mappedBy, obj.getId()).filterById(co.getId()).execute().assertTrue(rvo -> rvo.getEffectRowCount() <= 1, ServiceCode.ERROR, "更新一对多关系时数据错误");
                                    }

                                    db_fieldMap.remove(coField_id);
                                } else {// 数据库中不存在
                                    if (isPersist) {// 级联保存
                                        ReflectUtil.invokeSetter(mappedBy, co, obj);// 检查并确保外键连接
                                        coField_id = Database.getSession(this.spaceName).store(co).getId();
                                    } else { // 不是级联需要保存关系 2016-1-13 LHY   2017/3/7 改用update不用store,否则会新保存未保存的非级联对象,之前留下的天坑
                                        ReflectUtil.invokeSetter(mappedBy, co, obj);// 检查并确保外键连接
                                        Database.update((Class<Modelable>) co.getClass(), this.spaceName).assignColumn(mappedBy, obj.getId()).filterById(co.getId()).execute().assertTrue(rvo -> rvo.getEffectRowCount() <= 1, ServiceCode.ERROR, "更新一对多关系时数据错误");
                                    }

                                    if (null == coField_id) {
                                        throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, "在非级联状态下，一对多中的外键对象没有保存！");
                                    }
                                }

                                db_fieldMap.remove(coField_id);
                            }// end for collections
                        }// if L2 end
                        // 如果列表中还有对象，说明是前端删除的
                        if (hasCascade && cascade.contains(CascadeType.REMOVE))// 删除数据
                            for (Modelable co : db_fieldMap.values())
                                Database.getSession(this.spaceName).delete(co);
                        else
                            // 删除外键
                            for (Modelable co : db_fieldMap.values()) {
                                ReflectUtil.invokeSetter(mappedBy, co, null);
                                Database.getSession(this.spaceName).store(co);
                            }

                    } else {
//                // 清空，让list自动获取 LHY 2016-5-8 取消get方法自动获取功能后，这行就没用了
//                ReflectUtil.invokeSetter(fieldName, obj, null);
                        /******* 多对多 */
                        ManyToMany mtm = ReflectUtil.getAnnotation(beanClass, fieldName, ManyToMany.class);// 要么一对多，要么多对多
                        boolean hasCascade = null != mtm && mtm.cascade().length > 0;
                        List<CascadeType> cascade = hasCascade ? Arrays.asList(mtm.cascade()) : null;
                        boolean meIsPrimaryTable = mtm == null || !"void".equals(mtm.targetEntity().toString());

                        // 不为空并且 父对象不是标示为删除 则逐一保存
                        if (null != obj_fieldColl && notDelOper) { // if
                            // 2
                            boolean isPersist = hasCascade && cascade.contains(CascadeType.PERSIST);// 级联更新及保存
                            for (Modelable co : obj_fieldColl) {
                                // 多对多的多方为空则不用保存
                                if (null == co)
                                    continue;

                                String coField_id = String.valueOf(ReflectUtil.invokeGetter(SYS_id, co));

                                if (db_fieldMap.containsKey(coField_id)) {// 数据库中已经存在
                                    if (isPersist) {// 因为已经存在于数据库中，故为级联更新
                                        Database.getSession(this.spaceName).store(co);
                                    } else if (DataStatus.needSave.equalsString(co._getDataStatus())) {
                                        throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, "在非级联状态下，多对多中的外键对象没有保存更新！");
                                    }

                                    db_fieldMap.remove(coField_id);
                                } else {// 数据库中不存在
                                    if (isPersist) {// 级联保存
                                        coField_id = Database.getSession(this.spaceName).store(co).getId();
                                    }

                                    if (null != coField_id) {
                                        RsqlUtils.doManyToMany_insert(spaceName, beanClass, beanName, fieldName, obj_Id, coField_id, meIsPrimaryTable);
                                    } else {
                                        throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, "在非级联状态下，多对多中的外键对象没有保存！");
                                    }
                                }
                            }
                        }// if 2 end
                        // 如果列表中还有对象，说明是前端删除的
                        if (hasCascade && cascade.contains(CascadeType.REMOVE))// 级联删除
                            for (Modelable co : db_fieldMap.values())
                                Database.getSession().delete(co);
                        else
                            // 默认删除外键关系
                            for (Object F_id : db_fieldMap.keySet())
                                RsqlUtils.doManyToMany_delete(cvo._getSpaceName(), beanClass, beanName, fieldName, obj_Id, F_id, meIsPrimaryTable);
                    }
                    // TODO
                    // 由于struts调用的list对象的原始构造方法，采用ongl对list进行赋值。
                    // 故list中的对象绕过了AOPFactory，不能列入数据库缓存池中，所以在此把他清除
                    // 清除后，数据库的事务会发现list 为空，自动进行数据库查询，补充真实的bean进来
                    // ReflectUtil.invokeSetter(fieldName, obj, null);
                });
    }

    /**
     * 更新obj的dataStatus，标明数据的状态
     */
    private void updateDS(final Object obj, final Object dataStatus) {
        Method setter = ReflectUtil.getMethod(obj.getClass(), SYS_Method_setDataStatus);
        if (null != setter)
            ReflectUtil.invokeMethod(setter, obj, ReflectUtil.caseObject(setter.getGenericParameterTypes()[0], dataStatus));
        else
            throw new RsqlExecuteException(ServiceCode.RSQL_BEANSTATUS_ERROR, obj.getClass().toGenericString() + "保存了一个没有dataStatus的Bean，数据库数据会出现错误！");
    }

    /**
     * 更新obj的pk，因为每个数据保存的bean都应该明确了ormBean关系。其必有id属性{@link Modelable}<br>
     * ,这个函数是通过reflect更新其id
     */
    private void updatePK(final Object obj, final Object P_Id) {

        Method setter = ReflectUtil.getAllSetters(obj.getClass()).get(RsqlConstants.SYS_id);
        if (null != setter)
            ReflectUtil.invokeMethod(setter, obj, ReflectUtil.caseObject(setter.getGenericParameterTypes()[0], P_Id));
        else
            throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, obj.getClass().toString() + "保存了一个没有id的Bean，数据库数据会出现错误！");

    }

    /**
     * 判断数据状态，本数据库模型不支持没有状态的对象数据
     *
     * @return Object
     */
    private <T extends Modelable> String getDS(final T obj) {
        // // 不为空则检查id来判断是否保存
        // // 判断是否有getId方法
        // Method getter =
        // ReflectUtil.getAllGetters(obj.getClass()).get(SYS_dataStatus);
        //
        // // 没有getDataStatus方法的对象不能通过本数据库模型保存,库中没有，直接返回null
        // if (null == getter)
        // return null;
        //
        // // 有getgetDataStatus方法则调研该方法
        // String DS = (String) ReflectUtil.invokeMethod(getter, obj);
        // 2015-2-17
        return obj._getDataStatus();
    }

    /**
     * 判断目前数据库中是否包含参数对象<br>
     *
     * @param obj 需要检索的对象
     * @return String 对象存在主键id的值，<br>
     * 为true，否则将直接检查bean的id来验证 以下情况将返回null<br>
     * <p>obj == null <p>obj 没有getId() 和 setId() <p>obj 的属性id为空 <p>
     * 为true，否则将直接检查bean的id来验证
     * @throws Exception <p>如果数据库查询有错误<br> <p>或者本对象所对应的类在数据中持久化的表中的数据有重复项将会跑出异常<br>
     *                   如果数据中同样id的数据存在多条则数据完整性及结构存在问题<br>
     *                   一旦出现问题说明有人在程序外修改了数据结构。<br>
     *                   <p>
     *                   调用本方法通过判断返回值是否为空来确定是否在数据库中存在该对象。
     *                   的值，并通过query来检查是否在<br>
     *                   本数据库中存在该对象。
     */
    private <T extends Modelable> String getPK(final T obj, final boolean forceCheckFromDB) {
        String id;
        // 如果id为空则为未保存对象
        // id为空则表示是新对象，库中没有，返回fasle
        if (null == obj)
            return null;
        // 有getId方法则调研该方法
        Object idObj = obj.getId();// ReflectUtil.invokeGetter(SYS_id, obj); LHY
        // 2015-02-16
        // Object dataStatus = getDS(obj);

        id = (String) ReflectUtil.caseObject(String.class, idObj);
        /* 可以通过配置RDBConfignation.setCheckPKFULL来控制是否去数据库检查数据正确与否 */
        if (forceCheckFromDB && null != idObj) {

            // 如果id不为空，可能是数据库中的对象（标准状态id必须是从数据中获取的）
            // 通过id来检索该对象是否存在于本数据库
            DbRvo<T> qr = (DbRvo<T>) Database.createDbCvo(obj.getClass()).filterBy(Modelable::getId, WhereRuleOper.equal, idObj.toString()).ready().query();

            // 如果不在数据中
            if (qr.getRowCount() == 0)
                return null;
            else if (qr.getRecordCount() > 1)
                throw new RsqlExecuteException(ServiceCode.RSQL_DATA_ERROR, obj.getClass() + idObj.toString() + "主键ID重复！");
                // return null;// never arrived here
                // 如果在数据中唯一存在
            else
                // (qr.getRecordCount() == 1)数据库中有唯一项，则保存其id作为外键
                return (String) ReflectUtil.caseObject(String.class, qr.getCell(0, SYS_id));

        }
        return id;
    }

}
